using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Microblaze.Simulator.Engine;

namespace Weazel.Microblaze.Simulator.Components.Microblaze.Instructions
{
    public class Rsubi : Instruction
    {
        public enum RsubiInstructionKind
        {
            None,    // Error
            rsubikc, // add with carry, keep carry
            rsubic,  // add with carry
            rsubik,  // add keep carry
            rsubi
        }

        private RsubiInstructionKind kind;

        public override int CycleCount
        {
            get { return 1; }
        }

        public Rsubi()
            : base("rsubi")
        {
        }

        public override void Set(uint address, uint value)
        {
            base.Set(address, value);

            this.kind =
              (Opcode & 0x06) == 0x06 ? RsubiInstructionKind.rsubikc :
              (Opcode & 0x06) == 0x02 ? RsubiInstructionKind.rsubic :
              (Opcode & 0x06) == 0x04 ? RsubiInstructionKind.rsubik :
              (Opcode & 0x06) == 0x00 ? RsubiInstructionKind.rsubi :
               RsubiInstructionKind.None;

            if (kind == RsubiInstructionKind.None)
                throw new UnknownInstructionException(value);
        }

        /// <summary>
        /// The contents of register rA is subtracted from the value of IMM, 
        /// sign-extended to 32 bits, and the result is placed into register rD. 
        /// Bit 3 of the instruction (labeled as K in the figure) is set to a 
        /// one for the mnemonic rsubik. Bit 4 of the instruction (labeled as C 
        /// in the figure) is set to a one for the mnemonic rsubic. Both bits 
        /// are set to a one for the mnemonic rsubikc. When an rsubi instruction 
        /// has bit 3 set (rsubik, rsubikc), the carry flag will Keep its 
        /// previous value regardless of the outcome of the execution of the 
        /// instruction. If bit 3 is cleared (rsubi, rsubic), then the carry 
        /// flag will be affected by the execution of the instruction. When bit 
        /// 4 of the instruction is set to a one (rsubic, rsubikc), the content 
        /// of the carry flag (MSR[C]) affects the execution of the instruction. 
        /// When bit 4 is cleared (rsubi,rsubik), the content of the carry flag 
        /// does not affect the execution of the instruction (providing a normal 
        /// subtraction).
        /// </summary>    
        public override void Execute(MicroblazeComponent MicroblazeComponent)
        {
            bool c =
              kind == RsubiInstructionKind.rsubic ||
              kind == RsubiInstructionKind.rsubikc;

            bool k =
              kind == RsubiInstructionKind.rsubik ||
              kind == RsubiInstructionKind.rsubikc;

            uint carry = MicroblazeComponent.ArithmeticCarry ? (uint)1 : (uint)0;

            ulong notra = ~((ulong)MicroblazeComponent.ReadRegister(SourceRegisterA));

            ulong result = c == false ?
              (ulong)(GetImmediateValue(MicroblazeComponent)) + (ulong)(notra + 1) :
              (ulong)(GetImmediateValue(MicroblazeComponent)) + (ulong)(notra + carry);

            MicroblazeComponent.WriteRegister(DestinationRegister, (uint)result, Address);

            if (k)
            {
                MicroblazeComponent.ArithmeticCarry = result > 0xffffffff;
            }
        }

        public override string ToString()
        {
            return string.Format("{0} r{1}, r{2}, {3}",
              kind,
              DestinationRegister,
              SourceRegisterA,
              ImmediateValue
            );
        }
    }
}
